/*
 * @Author: your name
 * @Date: 2021-04-09 11:37:39
 * @LastEditTime: 2021-05-18 09:44:26
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /vite-project/src/utils/http/index.ts
 */

// axios配置  可自行根据项目进行更改，只需更改该文件即可，其他文件可以不动

import { HAxios } from './Axios';
import { AxiosTransform } from './axiosTransform';
import axios, { AxiosResponse } from 'axios';
import qs from 'qs';
import { Modal, message as Message } from 'ant-design-vue';
import { RequestEnum, ResultEnum, ContentTypeEnum } from '@/enums/httpEnum';

import { RequestOptions, Result } from './types';
import { isString } from '@/utils/is/index';
import { setObjToUrlParams } from '@/utils/urlUtils';
import { createNow, formatRequestDate } from './helper';

import router from '@/router';
import { useUserStoreWidthOut } from '@/store/modules/user';
import { storage } from '@/utils/Storage';

/**
 * @description: 数据处理，方便区分多种处理方式
 */
const transform: AxiosTransform = {
    /**
     * @description: 处理请求数据
     */
    transformRequestData: (
        res: AxiosResponse<Result>,
        options: RequestOptions
    ) => {
        const {
            isTransformRequestResult,
            isShowMessage = true,
            isShowErrorMessage = true,
            isShowSuccessMessage,
        } = options;

        const reject = Promise.reject;

        const { data } = res;
        //  这里 code，result，message为 后台统一的字段，需要在 types.ts内修改为项目自己的接口返回格式
        const { code, result, message } = data;
        // 请求成功 Reflect.has(target,name) --> es6语法：该方法的含义是：检查一个对象上是否含有特定的属性。相当于es5中的in操作符。那么参数 target: 就是改对象哦，name的含义是：该对象上的属性。
        // 判断code值是否为成功的code值，返回bool值
        const hasSuccess =
            data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
        // 是否显示提示信息
        if (isShowMessage) {
            if (hasSuccess && isShowSuccessMessage) {
                // 是否显示自定义提示信息
                Message.success(message || '请求成功！');
            } else if (!hasSuccess && isShowErrorMessage) {
                // 是否显示自定义提示信息
                Message.error(message || '请求失败！');
            }
        }

        // 不进行任何处理，直接返回
        // 用于页面代码可能需要直接获取code，data，message这些信息时开启
        if (!isTransformRequestResult) {
            return res.data;
        }

        if (!data) {
            // return '[HTTP] Request has no return value';
            return reject(data);
        }

        // 接口请求成功，直接返回结果
        if (code === ResultEnum.SUCCESS) {
            return result;
        }

        // 接口请求错误，统一提示错误信息
        if (code === ResultEnum.ERROR) {
            if (message) {
                Message.error(data.message);
                Promise.reject(new Error(message));
            } else {
                const msg = '操作失败,系统异常!';
                Message.error(msg);
                Promise.reject(new Error(msg));
            }
            return reject();
        }

        // 登录超时
        if (code === ResultEnum.TIMEOUT) {
            // 如果当前页面在登录页，不做其他处理
            if (router.currentRoute.value.name == 'login') return;
            // 回到登录页
            const timeoutMsg = '登录超时,请重新登录!';
            Modal.destroyAll();
            Modal.warning({
                title: '提示',
                content: '登录身份已失效,请重新登录!',
                onOk: () => {
                    router.replace({
                        name: 'login',
                        query: {
                            redirect: router.currentRoute.value.fullPath,
                        },
                    });
                    storage.clear();
                },
            });
            return reject(new Error(timeoutMsg));
        }
        // 这里逻辑可以根据项目进行修改
        if (!hasSuccess) {
            return reject(new Error(message));
        }

        return data;
    },

    /**
     * @description: 请求之前处理config
     */
    // 请求之前处理config
    // 请求之前处理config
    beforeRequestHook: (config, options) => {
        const {
            apiUrl,
            joinPrefix,
            joinParamsToUrl,
            formatDate,
            isParseToJson,
        } = options;

        config.url = `${apiUrl || ''}${config.url}`;

        if (config.method?.toUpperCase() === RequestEnum.GET) {
            const now = new Date().getTime();
            if (!isString(config.params)) {
                config.data = {
                    // 给 get 请求加上时间戳参数，避免从缓存中拿数据。
                    params: Object.assign(config.params || {}, {
                        _t: now,
                    }),
                };
            } else {
                // 兼容restful风格
                config.url = config.url + config.params + `?_t=${now}`;
                config.params = {};
            }
        } else {
            if (!isString(config.params)) {
                config.data = config.params;
                config.params = {};
                if (joinParamsToUrl) {
                    config.url = setObjToUrlParams(
                        config.url as string,
                        config.data
                    );
                }
            } else {
                // 兼容restful风格
                config.url = config.url + config.params;
                config.params = {};
            }
            // 'a[]=b&a[]=c'
            if (!isParseToJson) {
                config.params = qs.stringify(config.params, {
                    arrayFormat: 'brackets',
                });
                config.data = qs.stringify(config.data, {
                    arrayFormat: 'brackets',
                });
            }
        }
        return config;
    },

    /**
     * @description: 请求拦截器处理
     */
    requestInterceptors: (config) => {
        // 请求之前处理config
        const userStore = useUserStoreWidthOut();
        const token = userStore.getToken;
        if (token) {
            // jwt token
            config.headers.token = token;
        }
        return config;
    },

    /**
     * @description: 响应错误处理
     */
    responseInterceptorsCatch: (error: any) => {
        const { response, code, message } = error || {};
        const msg: string =
            response && response.data && response.data.error
                ? response.data.error.message
                : '';
        const err: string = error.toString();

        try {
            if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
                Message.error('接口请求超时,请刷新页面重试!');
                return;
            }
            if (err && err.includes('Network Error')) {
                Modal.confirm({
                    title: '网络异常',
                    content: '请检查您的网络连接是否正常!',
                });
                return;
            }
        } catch (error) {
            throw new Error(error);
        }

        return error;
    },
};

const Axios = new HAxios({
    timeout: 15 * 1000,
    headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
    // 数据处理方式
    transform,
    // 配置项，下面的选项都可以在独立的接口请求中覆盖
    requestOptions: {
        // 接口地址
        apiUrl: import.meta.env.API_URL as string,
        // 默认将prefix 添加到url
        joinPrefix: true,
        // 需要对返回数据进行处理
        isTransformRequestResult: true,
        // post请求的时候添加参数到url
        joinParamsToUrl: false,
        // 格式化提交参数时间
        formatDate: true,
    },
    withCredentials: false,
});

export default Axios;
